#include "../DEHT/deht.h"
#include "../global/global_declarations.h"
#include "../global/global_rainbow_table.h"
#include "../global/global_auth.h"
#include <stdlib.h>
#include <math.h>
#include "../exhaustive_search/rule.h"
#include "../exhaustive_search/dictionary.h"


void print_ini_file(IniFileContent* content) {
	printf("block_length = %d\n", content->bucket_block_length);
	printf("dictionary_filename = %s\n", content->dictionary_filename);
	printf("entries_in_hash_table = %ld\n", content->entries_in_hash_table);
	printf("main_random_seed = \"%s\"\n", content->main_random_seed);
	printf("chain_length = %d\n", content->rainbow_chain_length);
	printf("rule = %s\n", content->rule);
}

void write_file_1(IniFileContent* content, DEHT* d) {
	int num_of_seeds;
	int i, j;
	FILE* file1;
	int bytes_to_copy;
	char data[33000] = {0};
	DEHT_DISK_PTR bucket;
	read_seeds(d);
	num_of_seeds = d->header.numUnrelatedBytesSaved / sizeof(int64);
	file1 = fopen("text_export_output_1.txt", "w");
	fprintf(file1, "seeds:\n");
	for (i = 0; i < num_of_seeds; i++) {
		/* TODO: print in hex */
		fprintf(file1, "%ld\n", d->seeds[i]);
	}
	fprintf(file1, "\n\n");

	fprintf(file1, "passwords:\n");
	read_DEHT_pointers_table(d);

	for (i = 0; i < d->header.numEntriesInHashTable; i++) {
		bucket = d->hashTableOfPointersImageInMemory[i];
		if (bucket == 0) {
			continue;
		}
		while (true) {
			fseek(d->keyFP, bucket, SEEK_SET);
			for (j = 0; j < d->header.nPairsPerBlock; j++) {
				KeyFileRecord temp_record;
				fread(&temp_record, sizeof(KeyFileRecord), 1, d->keyFP);
				if (temp_record.validity == 1) {
					bytes_to_copy = temp_record.size_in_data;
					fseek(d->dataFP, ((DEHT_DISK_PTR)temp_record.offset_in_data_file_high << 32) | temp_record.offset_in_data_file_low, SEEK_SET);
					fread(data, 1, bytes_to_copy, d->dataFP);
					fwrite(data, 1, bytes_to_copy, file1);
					fprintf(file1, "\n");
				}
			}
			fread(&bucket, sizeof(DEHT_DISK_PTR), 1, d->keyFP);
			if (bucket == 0) {
				break;
			}
			else {
				continue; /*with bucket == next bucket in list*/
			}
		}
	}
	fclose(file1);
}

void write_hash_chain(int chain_length, unsigned char* first_pass, int first_pass_size, int64* seed_arr,
		BasicHashFunctionPtr hash_func, int hash_length, FILE* f) {
	int i;
	int64 temp_pass;
	String* str_pass = make_empty_string();
	unsigned char* cur_hash = calloc(hash_length, sizeof(unsigned char*));
	unsigned char* cur_hash_hex = calloc(hash_length*2, sizeof(unsigned char*));

	fwrite(first_pass, 1, first_pass_size, f);
	fprintf(f, "\t");

	hash_func((unsigned char*) first_pass, first_pass_size, cur_hash);

	for (i = 0; i < chain_length + 1; ++i) {
		binary2hexa(cur_hash, hash_length, (char*)cur_hash_hex, hash_length*2);
		fwrite(cur_hash_hex, 1, hash_length*2, f);
		fprintf(f, "\t%d\t", i);
		temp_pass = pseudo_random_function(cur_hash, hash_length, seed_arr[i]); /* reduction */
		int64_to_word(temp_pass, str_pass);
		fwrite(str_pass->data, 1, str_pass->size, f);
		fprintf(f, "\t");
		hash_func((unsigned char*) str_pass->data, str_pass->size,
				(unsigned char*) cur_hash);
	}

	String_free(str_pass);
	free(cur_hash);
}

void write_file_2(IniFileContent* content, DEHT* d) {
	int i, j;
	FILE* file2;
	int bytes_to_copy;
	unsigned char data[33000] = {0};
	DEHT_DISK_PTR bucket;
	read_seeds(d);
	file2 = fopen("text_export_output_2.txt", "w");
	read_DEHT_pointers_table(d);

	for (i = 0; i < d->header.numEntriesInHashTable; i++) {
		bucket = d->hashTableOfPointersImageInMemory[i];
		if (bucket == 0) {
			continue;
		}
		fprintf(file2, "bucket %d:\n", i);
		while (true) {
			fseek(d->keyFP, bucket, SEEK_SET);
			for (j = 0; j < d->header.nPairsPerBlock; j++) {
				KeyFileRecord temp_record;
				fread(&temp_record, sizeof(KeyFileRecord), 1, d->keyFP);
				if (temp_record.validity == 1) {
					bytes_to_copy = temp_record.size_in_data;
					fseek(d->dataFP, ((DEHT_DISK_PTR)temp_record.offset_in_data_file_high << 32) | temp_record.offset_in_data_file_low, SEEK_SET);
					fread(data, 1, bytes_to_copy, d->dataFP);

					write_hash_chain(content->rainbow_chain_length, data, bytes_to_copy, d->seeds,
							which_hash(d->header.sDictionaryName), hash_Length(d->header.sDictionaryName), file2);

					fprintf(file2, "\n");
				}
			}
			fread(&bucket, sizeof(DEHT_DISK_PTR), 1, d->keyFP);
			if (bucket == 0) {
				break;
			}
			else {
				continue; /*with bucket == next bucket in list*/
			}
		}
	}
	fclose(file2);
}


int main(int argc, char* argv[]) {
	IniFileContent content;
	bool retval;
	DEHT* d;
	char* prefix, *ini_filename;
	String* rule;

	if (argc != 3) {
		fprintf(stderr, "run with two command line arguments: prefix, ini_filename\n");
		exit(1);
	}


	prefix = argv[1];
	ini_filename = argv[2];

	retval = read_ini_file(&content, ini_filename);
	if (!retval) {
		perror(ini_filename);
	}
	rule = make_string_from_char_arr(content.rule);
	the_rule = make_rule_list_from_string(*rule);
	the_dictionary = create_dictionary_from_file(content.dictionary_filename);
	d = load_DEHT_from_files(prefix, str_mod, keyTo64Bit);

	print_ini_file(&content);

	write_file_1(&content, d);

	write_file_2(&content, d);



	return 0;
}
